There
are plenty of scenarios where orchestration is an unnecessary
addition to service bus processing. Recall that an orchestration's
prime benefit is the injection of a stateful, sequential series of
steps to message processing. If the "processing" of a message can
consist solely of the core BizTalk messaging components (receive ports,
send ports, pipelines, maps, and subscriptions), then a messaging-only
solution is the way to go. A general rule among BizTalk architects is
that you avoid orchestration and concentrate on pure messaging when at
all possible. This isn't because orchestration is intrinsically bad,
but rather, because many situations actually don't require the overhead
and complexity of a workflow.
That
said, how nicely does the BizTalk bus play with WCF services when no
orchestration is involved? Quite well, thank you. Let's look at how we
may route inbound data to a WCF subscriber using only the message bus.
In
our new scenario, we have a target service on our enterprise resource
planning (ERP) system that accepts new order notices and uses WCF's WsHttpBinding.
We'd like to call this service whenever a new product order enters the
MessageBox. This subscriber does not need any direct knowledge of the
upstream publisher or how the message arrived to the bus.
We
start this example by referencing the target service from within Visual
Studio.NET. This is done by once again right-clicking the BizTalk
project and choosing to Add and then Add Generated Items. The Consume WCF Service is selected and when the BizTalk WCF Service Consuming Wizard launches, the Metadata Exchange Endpoint is picked as the source of the service metadata. Now we plug in the valid HTTP endpoint URI of our WCF service.
When
the wizard completes, we have a set of metadata files (schemas,
bindings, orchestration) that help describe the service and how to
consume it. This example doesn't use orchestration, but we do still
require one hand-built BizTalk artifact a map. The WCF service expects
the order data to be in a particular format, so we should design a map
that gets applied by the outbound send port. The BizTalk map takes the
repeating set of order items and puts them into their appropriate
fields in the target schema.
Now we build and deploy our updated project. Once that deployment has succeeded, we import the binding generated by the BizTalk WCF Service Consuming Wizard. For this scenario, let's import the custom binding (OrderService_Custom.BindingInfo.xml), so that we can see the different properties this adapter exposes. A send port is created for the WCF-Custom adapter, but notice that the binding specified by the adapter matches that of our service endpoint: wsHttpBinding.
On the General tab of the adapter configuration, observe that there is a section called SOAP action header. This contains the SOAP action that will be attached to the outbound messages for this send port.
The BtsActionMapping wrapper in this textbox is used to support multiple SOAPActions values. However, this also means that something needs to set the BTS.Operation
value, which the WCF adapter uses to choose the appropriate SOAP action
from this collection. Typically this is set by the orchestration's
logical send port operation name, but in the absence of an
orchestration, we would have to set BTS.Operation
value in a custom pipeline component. Before throwing up your hands and
swearing loudly at this unnecessary complexity, breathe deeply and
relax. Luckily, this textbox also supports an alternate format that is
more supportive of simple content-based routing scenarios. A single
SOAP action URI in this textbox is used when you want to apply the same SOAPAction header to all messages transmitted by the send port. For our scenario, let's switch this to the single SOAPAction header that corresponds to our target service operation.
Next,
we must apply our XSLT map to this send port. The map is a required
component because our service is expecting a very specific data format
that does not match the original structure of the data.
Our final step is to introduce a valid subscription for this send port.
Without one, this port will never receive any messages published to the
bus. Because we'd like to pull every order from the MessageBox, our
subscription should be type-based, not context-based. A context-based
subscription would say that we are subscribing to properties of the
message (for example which port it arrived from) instead of anything
relating to the message itself. We want to subscribe to all messages of
a particular type. The subscription I applied can be seen in the
following screenshot:
Now,
technically we are finished. If we send a message into BizTalk, it
should route to a listening send port, and produce the expected tracing
statements. However, see the pitfall below to resolve an error with our current configuration.
Pitfall
The solution as it stands now will raise an exception. Why? The WCF
service that BizTalk calls does not return a response message. However,
the auto-generated BizTalk binding file was for a two-way send port.
So, when the WCF service is called, the request is successful, but we
end up with a subscription not found
exception on the (empty) response message. We can fix this by creating
a new one-way send port with identical configuration settings (adapter
type, URI, binding, SOAPAction, and subscription) to the auto-generated port.